/*! 
 * Header file for for the rigid body world structure.
 *
 * \author Devis Rossini
 * \date 14 April 2012
 */
#ifndef PHXSCENE_H_INCLUDE_GUARD
#define PHXSCENE_H_INCLUDE_GUARD

#include "PhxCollisionEnities.h"
#include "PhxCollisionAlgorithm.h"
#include "PhxCollisionAlgorithmManager.h"
#include "PhxContacts.h"
#include "PhxCollisionData.h"
#include "PhxForceRegistry.h"

#include <vector>

namespace phx
{
	/*! 
	 * A physical scene, considered as a collection of physical objects.
	 */
	class Scene
	{
	private:

		typedef std::vector<CollisionPrimitive*> CollisionPrimitives;
		
		/*!
         * Holds the primitives associated to this scene.
         */
		CollisionPrimitives			mPrimitives;

		/*!
		 * Fixed integration step.
		 */
		real						mIntegrationStep;

		/*!
		 * Accumulator for the remainder of the integration step.
		 */
		real						mPrevTime;

		/*! 
		 * Algorithm manager, used for retrieve the correct collision algorithm
		 * when a collision occurs.
		 */ 
		CollisionAlgorithmManager	mAlgorithmManager;

		/*!
		 * Maximum number of contacts handled by the collision resolver.
		 */ 
		unsigned					mMaxContacts;

		/*! 
		 * True if the world should calculate the number of iterations
		 * to give the contact resolver at each frame.
		 */
		bool						mCalculateIterations;
		
		/*!
		 * Collect all relevant information about collisions.
		 */
		CollisionData				mCollisionData;

		/*!
         * Holds the resolver for sets of contacts.
         */
        ContactResolver				mResolver;

		/*!
		 * Holds all the force generators and the bodies they apply to.
		 */
		ForceRegistry				mForceRegistry;

		// TODO: Add a contact registry.

	public:

		/*!
		 * Initialize a new scene.
		 */
		static Scene* CreateScene(unsigned iContacts = 256, unsigned iIterations = 0,
			real iDefaultFriction = real(0.9), real iDefaultRestitution = real(0.2), real iDefaultTolerance = real(0.1), 
			real iResolverPositionEpsilon = real(0.01), real iResolverVelocityEpsilon = real(0.01),
			real iIntegrationStep = real(0.01));

		/*!
		 * Initialize a new scene.
		 */
		static void DestroyScene(Scene* iScene);

		/*!
         * Add a specified primitive to the scene.
         */
		void AddPrimitive(CollisionPrimitive* iPrimitive);

		/*!
         * Create and add a new primitive to the scene.
         */
		CollisionPrimitive* CreatePrimitive(const CollisionPrimitiveDescriptor& iDesc);

		/*!
         * Add a new sphere to the scene.
         */
		void AddSphere(CollisionSphere* iSphere);

		/*!
         * Create and add a new sphere to the scene.
         */
		CollisionSphere* CreateSphere(const CollisionSphereDescriptor& iSphereDesc);

		/*!
         * Add a new plane to the scene.
         */
		void AddPlane(CollisionPlane* iPlane);

		/*!
         * Create and add a new plane to the scene.
         */
		CollisionPlane* CreatePlane(const CollisionPlaneDescriptor& iPlaneDesc);

		/*!
         * Add a new terrain to the scene.
         */
		void AddTerrain(CollisionTerrain* iTerrain);

		/*!
         * Create and add a new terrain to the scene.
         */
		CollisionTerrain* CreateTerrain(const CollisionTerrainDescriptor& iTerrainDesc);

		/*!
         * Add a new box to the scene.
         */
		void AddBox(CollisionBox* iBox);

		/*!
         * Create and add a new box to the scene.
         */
		CollisionBox* CreateBox(const CollisionBoxDescriptor& iBox);

		/*!
         * Remove a specified primitive to the physical scene.
         */
		void RemovePrimitive(CollisionPrimitive* iPrimitive);

		/*!
         * Destroy a specified primitive and remove it to the physical scene.
         */
		void DestroyPrimitive(CollisionPrimitive* iPrimitive);

		/*!
		 * Add a specified force generator, linked to a body.
		 */
		void AddForceGeneator(RigidBody* iBody, ForceGenerator* iForceGenerator);

		/*!
		 * Add a specified force generator, linked to a body.
		 */
		void AddForceGeneator(const ForceRegistration& iForceRegistration);

		/*!
		 * Remove a specificed force generator from the scene.
		 */
		void RemoveForceGenerator(RigidBody* iBody, ForceGenerator* iForceGenerator);

		/*!
		 * Remove a specificed force generator from the scene.
		 */
		void RemoveForceGenerator(const ForceRegistration& iForceRegistration);

		/*!
         * Initialises the world for a simulation frame. This clears
         * the force and torque accumulators for bodies in the
         * world. After calling this, the bodies can have their forces
         * and torques for this frame added.
         */
        void StartFrame();

        /*!
         * Processes all the physics for the world.
         */
        void RunPhysics(real iDuration);

		/*!
         * Processes all the physics for the world.
         */
        void RunPhysicsWithVariableStep(real iDuration);

		/*!
         * Processes all the physics for the world.
         */
		void SetDefaultFriction(real iFriction);
		/*!
         * Processes all the physics for the world.
         */
		const real GetDefaultFriction() const;

        /*!
         * Processes all the physics for the world.
         */
		void SetDefaultRestitution(real iRestitution);
        /*!
         * Processes all the physics for the world.
         */
		const real GetDefaultRestitution() const;

        /*!
         * Processes all the physics for the world.
         */
		void SetDefaultTolerance(real iTolerance);
        /*!
         * Processes all the physics for the world.
         */
		const real GetDefaultTolerance() const;

	private:

		/*! 
		 * Initialize the algorithm manager.
		 */
		void InitAlgorithmManager();

		/*! 
		 * Integrate a single frame.
		 */
		void Integrate(real iDuration);

		/*!
		 * Collision detection.
		 */
		unsigned GenerateContacts();

		/*! 
		 * Constructor.
		 */
		Scene(unsigned iContacts, unsigned iIterations,
			real iDefaultFriction, real iDefaultRestitution, real iDefaultTolerance, 
			real iResolverPositionEpsilon, real iResolverVelocityEpsilon,
			real iIntegrationStep);

		/*!
		 * Destructor.
		 */
		~Scene();
	};
}

#endif // PHXSCENE_H_INCLUDE_GUARD